using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.VersionControl.Client;
using Ltf.Remoting.TeamFoundation;
using System.IO;
using Ltf.Remoting.TFAccess;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace Ltf.Remoting.TFAccess
{
	public class LizardFileAccess
	{


		public static string GetFilePath(TFServerConnection connection, string path)
		{
			return GetFilePath(connection, path, string.Empty);
		}

		public static string GetFilePath(TFServerConnection connection, string path, String resultSuffix)
		{
			int changesetId;
			int deletionId;
			string name;
			LizardFileAccess.GetNameAndVersionFromPath(path, out name, out changesetId, out deletionId);

			return GetFilePath(connection, name, changesetId, deletionId, resultSuffix);
		}

		public static string GetFilePath(TFServerConnection connection, string path, int changesetId, string resultSuffix)
		{
			return GetFilePath(connection, path, changesetId, 0, resultSuffix);
		}

		public static string GetFilePath(TFServerConnection connection, string path, int changesetId, int deletionId, string resultSuffix)
		{
			if (path.StartsWith("$"))
			{
				if (connection == null)
					throw new ArgumentNullException("Connection can not be null when 'path' is a server path.");
				VersionControlServer vcServer = connection.VersionControlServer;
				TFAccess.TFDifRepository repo = (TFAccess.TFDifRepository)GetRepositoryForPath(connection, path, changesetId, deletionId);
				if (repo.FileExists(path))
				{
					string tempFile = ServerConnections.DefaultConnection.Settings.TempPath + "\\"
						+ Path.GetFileName(path) + "." + repo.Version + "." + resultSuffix;
					using (Stream s = repo.GetFileItemStream(path))
					{
						using (StreamReader sr = new StreamReader(s))
						{
							File.WriteAllText(tempFile, sr.ReadToEnd());
						}
						//byte[] buffer = new byte[255];
						//using (FileStream fs = File.OpenWrite(tempFile))
						//{
						//  int i;
						//  while ((i = s.Read(buffer, 0, 255)) > 0)
						//  {
						//    fs.Write(buffer, 0, i);
						//  }
						//}
					}
					return tempFile;
				}
				throw new System.IO.FileNotFoundException();
			}
			else
				return path;
		}

		public static string GetLocalTFVersionFilePath(string path)
		{
			TFServerConnection connection = ServerConnections.GetConnectionForPath(path);
			TFAccess.LocalTFDifRepository localTf = new LocalTFDifRepository(connection.VersionControlServer, connection.Settings.TempPath);
			if (localTf.FileExists(path))
			{
				string tempFile = connection.Settings.TempPath + "\\"
					+ Path.GetFileName(path) + ".tflocalversion";
				using (Stream s = localTf.GetFileItemStream(path))
				{
					byte[] buffer = new byte[255];
					using (FileStream fs = File.OpenWrite(tempFile))
					{
						int i;
						while ((i = s.Read(buffer, 0, 255)) > 0)
						{
							fs.Write(buffer, 0, i);
						}
					}
				}
				return tempFile;
			}
			throw new System.IO.FileNotFoundException();

		}

		//public static IDifRepository GetRepositoryForPath(string path, string versionText)
		//{
		//  int version = GetChangesetIdFromVersionText(versionText);
		//  return GetRepositoryForPath(path, version, 0);
		//}

		public static int GetChangesetIdFromVersionText(string versionText)
		{
			int version = 0;
			if (versionText == "local")
				version = -1;
			else
				int.TryParse(versionText, out version);
			return version;
		}

		public static IDifRepository GetRepositoryForPath(TFServerConnection connection, string path, string versionText)
		{
			int version = GetChangesetIdFromVersionText(versionText);
			return GetRepositoryForPath(connection, path, version, 0);
		}


		//public static IDifRepository GetRepositoryForPath(string path, int changesetId, int deletionId)
		//{
		//  return GetRepositoryForPath(ServerConnections.DefaultConnection, path, changesetId, deletionId);
		//}

		public static IDifRepository GetRepositoryForPath(TFServerConnection connection, string path, int changesetId, int deletionId)
		{
			if (path.StartsWith("$"))
			{
				if (connection == null)
					throw new ArgumentNullException("Connection can not be null when 'path' is a server path.");
				VersionControlServer vcServer = connection.VersionControlServer;
				if (changesetId == -1)
				{
					TFAccess.LocalTFDifRepository localRepo = new LocalTFDifRepository(vcServer, connection.Settings.TempPath);
					return localRepo;
				}
				TFAccess.TFDifRepository tfRepo = new TFAccess.TFDifRepository(vcServer, connection.Settings.TempPath);
				if (changesetId >= 0)
				{
					tfRepo.Version = changesetId;
					tfRepo.DeletionId = deletionId;
				}

				return tfRepo;
			}
			else
			{
				return new FSDifRepository();
			}
		}

		public static string RevisionPath(string path, int newVersion)
		{
			string name;
			int v;
			GetNameAndVersionFromPath(path, out name, out v);

			return LizardFileAccess.SetPathVersion(name, newVersion);
		}

		public static void LoadVersions(TFServerConnection connection, ComboBox cmbVersion, string path)
		{
			int v;
			int deletionId;
			string name;
			GetNameAndVersionFromPath(path, out name, out v, out deletionId);
			LoadVersions(connection, cmbVersion, name, v, deletionId);
		}

		private static List<ComboBox> ActivelyLoadingBoxes = new List<ComboBox>();

		public static bool IsActivelyLoading(ComboBox cmbVersion)
		{
			return ActivelyLoadingBoxes.Contains(cmbVersion);
		}

		public static void LoadVersions(TFServerConnection connection, ComboBox cmbVersion, string path, int changesetId, int deletionId)
		{
			int currentVersion = 0;
			int.TryParse(cmbVersion.Text, out currentVersion);
			bool latest = (cmbVersion.Text.ToLower() == "latest" || string.IsNullOrEmpty(cmbVersion.Text));
			if (path == "All")
				return;
			//int changesetId = 0;
			//int deletionId = 0;
			//string name = string.Empty;
			//LizardFileAccess.GetNameAndVersionFromPath(path,out  name, out changesetId, out deletionId);

			if (ActivelyLoadingBoxes.Contains(cmbVersion))
				return;

			ActivelyLoadingBoxes.Add(cmbVersion);
			try
			{
				VersionControlServer vcServer = connection.VersionControlServer;
				VersionSpec versionSpec;
				if (changesetId == 0)
					versionSpec = ChangesetVersionSpec.Latest;
				else
					versionSpec = new ChangesetVersionSpec(changesetId);
				cmbVersion.Items.Clear();

				bool isItemSelected = false;

				ExtendedItem[] ita = vcServer.GetExtendedItems(new ItemSpec[] { new ItemSpec(path, RecursionType.None) }, DeletedState.Deleted, ItemType.Any)[0];
				List<int> DeletionChangesets = new List<int>();
				foreach (ExtendedItem extItem in ita)
					DeletionChangesets.Add(extItem.VersionLatest);

				int latestChangesetId = vcServer.GetLatestChangesetId();
				object history = vcServer.QueryHistory(path, versionSpec, deletionId, RecursionType.Full, "",
					new ChangesetVersionSpec(1), new ChangesetVersionSpec(latestChangesetId), 200, true, false);
				System.Collections.IEnumerable changesets = (System.Collections.IEnumerable)history;

				Changeset first = null;
				foreach (Changeset c in changesets)
				{
					if (first == null)
						first = c;
					if (!DeletionChangesets.Contains(c.ChangesetId))
					{
						cmbVersion.Items.Add(c.ChangesetId);
						if (currentVersion == c.ChangesetId)
						{
							cmbVersion.SelectedIndex = cmbVersion.Items.Count - 1;
							isItemSelected = true;
						}
					}
				}
				if (first == null || !DeletionChangesets.Contains(first.ChangesetId))
				{
					int pi = cmbVersion.SelectedIndex;
					cmbVersion.Items.Insert(0, "Latest");
					if (cmbVersion.SelectedIndex == pi)
					{
						int nextIdx = pi + 1;
						cmbVersion.SelectedIndex= nextIdx;
					}
				}
				if (!isItemSelected && cmbVersion.Items.Count > 0)
					cmbVersion.Text = changesetId.ToString();
				int pib = cmbVersion.SelectedIndex;
				cmbVersion.Items.Insert(1, "Browse...");
				if (cmbVersion.SelectedIndex > 0 && cmbVersion.SelectedIndex == pib)
					cmbVersion.SelectedIndex++;
				if (latest)
					cmbVersion.SelectedIndex = 0;
			}
			catch (Exception ex)
			{

			}
			if (ActivelyLoadingBoxes.Contains(cmbVersion))
				ActivelyLoadingBoxes.Remove(cmbVersion);

		}

		public static void GetNameAndVersionSpecFromPath(string path, out string name, out VersionSpec versionSpec)
		{
			versionSpec = VersionSpec.Latest; ;
			if (path.IndexOf(';') > 0)
			{
				string[] parts = path.Split(';');
				name = parts[0];
				string versionQualifier = parts[1];
				if (versionQualifier.Length > 1)
				{
					string versionType = versionQualifier.Substring(0, 1).ToLower();
					string versionIdent = versionQualifier.Substring(1);
					switch (versionType)
					{
						case "t":
							break;
						case "c":
							int changeSetId;
							if (int.TryParse(versionIdent, out changeSetId))
								versionSpec = new ChangesetVersionSpec(changeSetId);
							break;
						case "l":
							string label = versionIdent;
							versionSpec = new LabelVersionSpec(label);
							break;
						case "d":
							DateTime date;
							if (DateTime.TryParse(versionIdent, out date))
								versionSpec = new DateVersionSpec(date);
							break;
					}
				}
			}
			else
			{
				name = path;
			}

			return;
		}

		public static string SetPathVersion(string path, int version)
		{
			if (!path.StartsWith("$"))
				return path;

			string name;
			int v;
			int d;
			bool hasDeletionId;
			GetNameAndVersionFromPath(path, out name, out v, out d, out hasDeletionId);
			if (!hasDeletionId)
			{
				path = string.Format("{0};c{1}", name, version);
			}
			else
			{
				path = string.Format("{0};c{1};d{2}", name, version, d);
			}
			path = path.Replace('\\', '/');

			return path;
		}

		public static string SetPathDeletionId(string path, int deletionId)
		{
			string name;
			int v;
			int d;
			bool hasDeletionId;
			GetNameAndVersionFromPath(path, out name, out v, out d);
			path = string.Format("{0};c{1};d{2}", name, v, deletionId);
			path = path.Replace('\\', '/');
			return path;
		}

		public static string SetPathVersionAndDeletionId(string path, int version, int deletionId)
		{
			string name;
			int d;
			int v;
			GetNameAndVersionFromPath(path, out name, out v, out d);
			path = string.Format("{0};c{1};d{2}", name, version, deletionId);
			path = path.Replace('\\', '/');
			return path;
		}

		public static void GetNameAndVersionFromPath(string path, out string name, out int version)
		{
			int deletionId = 0;
			bool hasDeletionId;
			GetNameAndVersionFromPath(path, out name, out version, out deletionId, out hasDeletionId);
		}

		/// <summary>
		/// Gets the name and version from path.
		/// </summary>
		/// <param name="path">The path.</param>
		/// <param name="name">The name.</param>
		/// <param name="version">The version.</param>
		/// <param name="deletionId">The deletion id.</param>
		public static void GetNameAndVersionFromPath(string path, out string name, out int version, out int deletionId)
		{
			deletionId = 0;
			name = path;
			bool hasDeletionId = false;
			GetNameAndVersionFromPath(path, out  name, out  version, out  deletionId, out  hasDeletionId);
		}

		public static void GetNameAndVersionFromPath(string path, out string name, out int version, out int deletionId, out bool hasDeletionId)
		{
			int v = 0;
			deletionId = 0;
			name = path;
			hasDeletionId = false;
			string[] pv = path.Split(new string[] { ";c" }, StringSplitOptions.RemoveEmptyEntries);
			if (pv.Length >= 2)
			{
				name = pv[0];
				string[] pv2 = pv[1].Split(new string[] { ";d" }, StringSplitOptions.RemoveEmptyEntries);
				if (!int.TryParse(pv2[0], out v))
				{
					if (pv2[0] == "local")
						v = -1;
					if (pv2[0] == "latest")
						v = 0;
				}
				if (pv2.Length >= 2)
				{
					hasDeletionId = true;
					int.TryParse(pv2[1], out deletionId);
				}
			}
			version = v;
		}



		public static string GetWorkingBaseRepoPath(string filePath)
		{
			Workspace ws = ServerConnections.GetConnectionForPath(filePath).GetWorkspace(filePath);
			string local = ws.GetServerItemForLocalItem(filePath);

			return local;
		}

		public static bool RepoPathExists(string path)
		{
			string name;
			int v;
			GetNameAndVersionFromPath(path, out name, out v);
			//TODO: will require a connection!
			VersionControlServer vcs = ServerConnections.DefaultConnection.VersionControlServer;
			try
			{
				Item i = vcs.GetItem(name, new ChangesetVersionSpec(v));
			}
			catch
			{
				return false;
			}
			return true;
		}

		public static string GetLTFMetaDataFileName(string fileName)
		{
			string folder = Path.GetDirectoryName(fileName);
			string ltfMetaDataPath = ServerConnections.GetConnectionForPath(fileName).GetMetaFolder(folder);
			string ltfMetaDataFileName = Path.Combine(ltfMetaDataPath, Path.GetFileName(fileName) + ".ltfvc");

			return ltfMetaDataFileName;
		}

		public static string GetLTFLocalCacheFileName(string fileName)
		{
			string folder = Path.GetDirectoryName(fileName);
			string ltfMetaDataPath = ServerConnections.GetConnectionForPath(fileName).GetMetaFolder(folder);
			string ltfMetaDataFileName = Path.Combine(ltfMetaDataPath, Path.GetFileName(fileName) + ".server");

			return ltfMetaDataFileName;
		}

		public static LizardFileInfo GetLizardFileInfo(string fileName)
		{
			LizardFileInfo fileInfo = null;
			if (fileName != null)
			{
				string metaFileName = GetLTFMetaDataFileName(fileName);
				if (!string.IsNullOrEmpty(fileName) && File.Exists(metaFileName))
				{
					XmlSerializer xs = new XmlSerializer(typeof(LizardFileInfo));
					using (Stream s = new FileStream(metaFileName, FileMode.Open))
					{
						fileInfo = (LizardFileInfo)xs.Deserialize(s);
					}
					return fileInfo;
				}
			}
			fileInfo = new LizardFileInfo();
			fileInfo.OverlayType = LizardOverLayStatus.None;
			fileInfo.Status = LizardFileStatus.Same;
			fileInfo.LocalVersion = 0;
			fileInfo.ServerVersion = 0;
			return fileInfo;

		}

		public static string GetPathEnd(string basePath, string path)
		{
			string result = path.Substring(basePath.Length + 1);
			return result;
		}

		public static string PathConcat(string rootPart, string tailPart)
		{
			string concat = rootPart;
			if (tailPart.StartsWith("\\") || tailPart.StartsWith("/"))
				tailPart = tailPart.Substring(1);

			if (rootPart.StartsWith("$"))
			{
				concat += "/" + tailPart.Replace("\\", "/");
			}
			else
			{
				concat += "\\" + tailPart.Replace("/", "\\");
			}
			return concat;
		}
		
	}
}
